package com.bac.rentmap.distance;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * Calculates distance in kilometers between points by lat and lon.
 * 
 * @author Andrew Osipenko
 */
public class DistanceCalculator {
	private static final Logger log = Logger.getLogger(DistanceCalculator.class
			.getName());
	private static final double EARTH_RADIUS_KM = 6371;

	/**
	 * Calculate distances between point with latitude <code>lat</code> and
	 * longitude <code>lon</code> and <code>distanceCheckPoints</code>.
	 * 
	 * @param lat
	 *            latitude
	 * @param lon
	 *            longitude
	 * @param distanceCheckPoints
	 *            required
	 * @return not null list of distances in kilometers.
	 */
	public List<Double> calculateDistance(double lat, double lon,
			List<DistanceCheckPoint> distanceCheckPoints) {
		List<Double> res = new ArrayList<Double>();
		for (DistanceCheckPoint distanceCheckPoint : distanceCheckPoints) {
			res.add(calculateDistance(lat, lon, distanceCheckPoint));
		}
		return res;
	}

	public int findNearestDistanceCheckPointIndex(double lat, double lon,
			List<DistanceCheckPoint> distanceCheckPoints) {
		int res = -1;
		double minDistance = Double.MAX_VALUE;

		for (int i = 0; i < distanceCheckPoints.size(); i++) {
			double distance = calculateDistance(lat, lon, distanceCheckPoints
					.get(i));
			if (distance < minDistance) {
				minDistance = distance;
				res = i;
			}
		}
		log.fine("Nearest check point " + distanceCheckPoints.get(res)
				+ " with index " + res + ", distance to it is " + minDistance
				+ " km");
		return res;
	}

	private double calculateDistance(double lat, double lon,
			DistanceCheckPoint distanceCheckPoint) {
		double latRadians = toRadians(distanceCheckPoint.getLat());
		double lonRadians = toRadians(distanceCheckPoint.getLon());
		lat = toRadians(lat);
		lon = toRadians(lon);
		// cos(d) = sin(φА)·sin(φB) + cos(φА)·cos(φB)·cos(λА −
		// λB),
		double d = Math.acos(Math.sin(lat) * Math.sin(latRadians)
				+ Math.cos(lat) * Math.cos(latRadians)
				* Math.cos(lon - lonRadians));

		// double dlat = lat - distanceCheckPoint.getLat();
		// double dlon = lon - distanceCheckPoint.getLon();
		// return Math.sqrt(dlat * dlat + dlon * dlon);
		return d * EARTH_RADIUS_KM;
	}

	private double toRadians(double grads) {
		return grads / 180 * Math.PI;
	}
}
